#pragma once

#include <efi/efi.hpp>

#define EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID                                   \
  {                                                                            \
    0x2F707EBB, 0x4A1A, 0x11d4, {                                              \
      0x9A, 0x38, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D                           \
    }                                                                          \
  }

#define EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO BIT(0)
#define EFI_PCI_ATTRIBUTE_ISA_IO BIT(1)
#define EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO BIT(2)
#define EFI_PCI_ATTRIBUTE_VGA_MEMORY BIT(3)
#define EFI_PCI_ATTRIBUTE_VGA_IO BIT(4)
#define EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO BIT(5)
#define EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO BIT(6)
#define EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE BIT(7)
#define EFI_PCI_ATTRIBUTE_MEMORY_CACHED BIT(11)
#define EFI_PCI_ATTRIBUTE_MEMORY_DISABLE BIT(12)
#define EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE BIT(15)
#define EFI_PCI_ATTRIBUTE_ISA_IO_16 BIT(16)
#define EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16 BIT(17)
#define EFI_PCI_ATTRIBUTE_VGA_IO_16 BIT(18)

namespace efi {

typedef struct PciRootBridgeIoProtocol {
  enum class OperationWidth : uint32_t {
    Uint8,
    Uint16,
    Uint32,
    Uint64,
    FifoUint8,
    FifoUint16,
    FifoUint32,
    FifoUint64,
    FillUint8,
    FillUint16,
    FillUint32,
    FillUint64
  };

  enum class Operation : uint32_t {
    BusMasterRead,
    BusMasterWrite,
    BusMasterCommonBuffer,
    BusMasterRead64,
    BusMasterWrite64,
    BusMasterCommonBuffer64
  };

  typedef Status(EFIAPI *Poll)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN OperationWidth width,
      IN uintptr_t address,
      IN uint64_t mask,
      IN uint64_t value,
      IN uint64_t delay,
      OUT uint64_t *result
  );

  typedef Status(EFIAPI *IoOperation)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN OperationWidth Width,
      IN uintptr_t address,
      IN uint64_t count,
      IN OUT void *buffer
  );

  typedef Status(EFIAPI *CopyMem)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN OperationWidth width,
      IN uintptr_t destAddress,
      IN uintptr_t srcAddress,
      IN uint64_t count
  );

  typedef Status(EFIAPI *Map)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN Operation operation,
      IN void *hostAddress,
      IN OUT uint64_t *numberOfBytes,
      OUT uintptr_t *deviceAddress,
      OUT void **mapping
  );

  typedef Status(EFIAPI *Unmap)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN void *mapping
  );

  typedef Status(EFIAPI *AllocateBuffer)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN Memory::AllocationType allocationType,
      IN Memory::Type memoryType,
      IN uint64_t pageCount,
      OUT void **hostAddress,
      IN uint64_t attributes
  );

  typedef Status(EFIAPI *FreeBuffer)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN uint64_t pageCount,
      IN void *hostAddress
  );

  typedef Status(EFIAPI *Flush)(IN struct PciRootBridgeIoProtocol *instance);

  typedef Status(EFIAPI *GetAttributes)(
      IN struct PciRootBridgeIoProtocol *instance,
      OUT uint64_t *supports OPTIONAL,
      OUT uint64_t *attributes OPTIONAL
  );

  typedef Status(EFIAPI *SetAttributes)(
      IN struct PciRootBridgeIoProtocol *instance,
      IN uint64_t attributes,
      IN OUT uintptr_t *resourceBase OPTIONAL,
      IN OUT uint64_t *resourceLength OPTIONAL
  );

  typedef Status(EFIAPI *GetConfiguration)(
      IN struct PciRootBridgeIoProtocol *instance,
      OUT void **resources
  );

  typedef struct {
    IoOperation read;
    IoOperation write;
  } PciAccessor;

  Handle parentHandle;
  Poll pollMem;
  Poll pollIo;
  PciAccessor mmio;
  PciAccessor io;
  PciAccessor pci;
  CopyMem copyMem;
  Map map;
  Unmap unmap;
  AllocateBuffer allocateBuffer;
  FreeBuffer freeBuffer;
  Flush flush;
  GetAttributes getAttributes;
  SetAttributes setAttributes;
  GetConfiguration getConfiguration;
  uint32_t segmentNumber;
} PciRootBridgeIoProtocol;

} // namespace efi